typedef sim_size	tlb_vaddr;
typedef sim_size	tlb_paddr;
typedef uint32_t	tlb_mask;

typedef struct tlb_entry tlb_entry;

struct tlb_entry {
	uint8_t		global;		// Is entry global (same as PFN_GLOBAL in frame0/frame1)

	tlb_vaddr	vpn;		// Virtual page number
	tlb_vaddr	page_mask;	// Mask to set page size
	tlb_paddr	frame0;		// PFN 1
	tlb_paddr	frame1;		// PFN 2
};

#ifdef MIPS64
	#define TLB_VPN_R			62
	#define TLB_PFN_MASK_4K		0xFFFFFFFFFFFFE000LL	
	#define TLB_VPN_MASK_4K		0xFFFFFFFFFFFFC000LL
	#define TLB_IDX_MASK_4K		0x2000
#else /* !MIPS64 */
	#define TLB_VPN_R			30
	#define TLB_PFN_MASK_4K		0xFFFFE000L		
	#define TLB_VPN_MASK_4K		0xFFFFC000L
	#define TLB_IDX_MASK_4K		0x2000
#endif /* !MIPS64 */

#define TLB_PFN_MASK(m)			(TLB_PFN_MASK_4K & ~(m))
#define TLB_VPN_MASK(m)			(TLB_VPN_MASK_4K & ~(m) & ~((m)+1))
#define TLB_IDX_MASK(m)			((m)+1)

#define TLB_VPN_XUSEG	(0 << TLB_VPN_R)
#define TLB_VPN_XSSEG	(1 << TLB_VPN_R)
#define TLB_VPN_XKPHYS	(2 << TLB_VPN_R)
#define TLB_VPN_XKSEG	(3 << TLB_VPN_R)

#define VPN_ASID		0xFF
#define GET_ASID(v)		(v & VPN_ASID)
#define SET_ASID(v, a)	((v) = ((v) & ~VPN_ASID) | ((a) & VPN_ASID))

#define PFN_GLOBAL		0x1
#define PFN_VALID		0x2
#define PFN_DIRTY		0x4
#define PFN_CACHE		0x38

struct tlb {
	sim_size	nslots;
	sim_size	entries;
	
	tlb_entry	*slots;
};

/****
 * tlb_create - Create a new TLB with a given number of slots
 *
 * t		- The TLB to create'
 * slots	- The number of slots in the TLB
 *
 * Returns E_OKAY on success, E_SYS_NOMEM on error
 **/
tlb_error
create_tlb(	TLB *t,
			sim_size slots);

/****
 * tlb_destroy - Deallocate an existing TLB
 *
 * t		- The TLB to destroy
 *
 * Returns E_OKAY
 **/
tlb_error
destroy_tlb(TLB *t);

/****
 * tlb_lookup - Translates an address using the TLB
 *
 * t		- The TLB to preform the lookup in
 * vaddr	- The virtual address to lookup
 * asid		- The address space ID
 * paddr	- A location to store the physical address to if found (if != NULL)
 * flags	- A location to store flags pertaining to the TLB entry to (if != NULL)
 *
 * Returns E_TLB_HIT on success, E_TLB_MISS if entry not found
 **/
tlb_error
tlb_lookup(	TLB *t,
			tlb_vaddr vaddr,
			sim_size asid,
			tlb_paddr *paddr,
			tlb_paddr *flags);


/****
 * tlb_search - Search for a virtual address in the TLB and get the index
 *
 * t		- The TLB to search in
 * vaddr	- The virtual page number (including ASID) to look for
 * index	- A location to store the index found to
 *
 * Returns E_TLB_HIT on success, E_TLB_MISS if address not found
 **/
tlb_error
tlb_search(	TLB *t,
			tlb_vaddr vaddr,
			sim_size *index);

/****
 * tlb_set - Set the data stored at a given index in the TLB
 *
 * t		- The TLB to store to
 * index	- The index to store at
 * vpn		- The VPN (including ASID) to store
 * mask		- The page mask to store
 * frame0	- The first PFN (including flags)
 * frame1	- The second PFN (including flags, global flags should match frame0)
 *
 * Returns E_OKAY on success, E_INVALID if index >= number of slots
 **/
tlb_error
tlb_set(	TLB *t,
			sim_size index,
			tlb_vaddr vpn,
			tlb_vaddr mask,
			tlb_paddr frame0,
			tlb_paddr frame1);

/****
 * tlb_get - Get the data stored at a given index in the TLB
 *
 * t		- The TLB to load from
 * index	- The index to load from
 * vpn		- If not NULL is set to the VPN
 * mask		- If not NULL is set to the page mask for the entry
 * frame0	- If not NULL is set to the first PFN
 * frame1	- If not NULL is set to the second PFN
 *
 * Returns E_OKAY on success, E_INVALID if index >= number of slots
 **/
tlb_error
tlb_get(	TLB *t,
			sim_size index,
			tlb_vaddr *vpn,
			tlb_vaddr *mask,
			tlb_paddr *frame0,
			tlb_paddr *frame1);

